import pandas as pd
import matplotlib

# 设置非交互式后端以避免GUI相关错误
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import mplfinance as mpf
from datetime import datetime
import os

# 尝试多种设置中文字体的方法
# 方法1：设置字体族
plt.rcParams['font.family'] = ['SimHei', 'WenQuanYi Micro Hei', 'Heiti TC', 'Microsoft YaHei', 'Arial Unicode MS', 'sans-serif']
# 方法2：设置sans-serif字体列表
plt.rcParams['font.sans-serif'] = ['SimHei', 'WenQuanYi Micro Hei', 'Heiti TC', 'Microsoft YaHei', 'Arial Unicode MS', 'sans-serif']
# 方法3：设置全局字体
plt.rcParams['axes.titlesize'] = 16
plt.rcParams['axes.labelsize'] = 12
plt.rcParams['xtick.labelsize'] = 10
plt.rcParams['ytick.labelsize'] = 10
# 确保负号正常显示
plt.rcParams['axes.unicode_minus'] = False

# 为mplfinance设置字体配置
# 创建一个style对象用于mplfinance图表
mpf_style = mpf.make_mpf_style(base_mpf_style='charles',
                               rc={'font.family': ['SimHei', 'WenQuanYi Micro Hei', 'Heiti TC', 'Microsoft YaHei', 'Arial Unicode MS', 'sans-serif'],
                                   'axes.unicode_minus': False})

# 全局设置matplotlib参数，确保所有图表都能显示中文
matplotlib.rcParams.update({
    'font.family': ['SimHei', 'WenQuanYi Micro Hei', 'Heiti TC', 'Microsoft YaHei', 'Arial Unicode MS', 'sans-serif'],
    'axes.unicode_minus': False
})

class BYDStockAnalyzer:
    def __init__(self, file_path):
        self.file_path = file_path
        self.data = None
        self.figures_dir = 'figures'
        
        # 确保figures目录存在
        if not os.path.exists(self.figures_dir):
            os.makedirs(self.figures_dir)
    
    def load_data(self):
        """加载Excel数据"""
        print(f"正在加载数据: {self.file_path}")
        self.data = pd.read_excel(self.file_path)
        
        # 检查数据结构
        print(f"数据加载完成，共{len(self.data)}行")
        print("数据前5行:")
        print(self.data.head())
        print("\n数据列名:")
        print(self.data.columns)
        
        # 假设日期列的名称可能是'日期'或包含'date'的列
        date_columns = [col for col in self.data.columns if '日期' in str(col) or 'date' in str(col).lower()]
        
        if date_columns:
            date_column = date_columns[0]
            # 转换日期列
            if not pd.api.types.is_datetime64_any_dtype(self.data[date_column]):
                try:
                    self.data[date_column] = pd.to_datetime(self.data[date_column])
                except:
                    print(f"无法将列'{date_column}'转换为日期格式")
            
            # 设置日期为索引
            self.data.set_index(date_column, inplace=True)
            print(f"已设置'{date_column}'为索引")
        
        return self.data
    
    def clean_data(self):
        """数据清洗"""
        if self.data is None:
            raise ValueError("请先加载数据")
            
        # 处理缺失值
        print(f"清洗前数据形状: {self.data.shape}")
        self.data = self.data.dropna()
        print(f"清洗后数据形状: {self.data.shape}")
        
        # 排序数据
        if isinstance(self.data.index, pd.DatetimeIndex):
            self.data = self.data.sort_index()
            
        return self.data
    
    def identify_price_columns(self):
        """识别价格相关列"""
        # 常见的价格列名称
        price_keywords = ['开盘', 'open', 'Open', '收盘价', 'close', 'Close', 'high', 'High', 'low', 'Low', 'volume', '成交量']
        price_columns = {}
        
        for col in self.data.columns:
            col_str = str(col)
            if any(keyword in col_str.lower() for keyword in ['close', '收盘']):
                price_columns['close'] = col
            elif any(keyword in col_str.lower() for keyword in ['open', '开盘']):
                price_columns['open'] = col
            elif any(keyword in col_str.lower() for keyword in ['high', '最高']):
                price_columns['high'] = col
            elif any(keyword in col_str.lower() for keyword in ['low', '最低']):
                price_columns['low'] = col
            elif any(keyword in col_str.lower() for keyword in ['volume', '成交量']):
                price_columns['volume'] = col
        
        print("识别的价格列:")
        for key, value in price_columns.items():
            print(f"{key}: {value}")
        
        return price_columns
    
    def calculate_indicators(self, price_columns):
        """计算技术指标"""
        if 'close' in price_columns:
            close_col = price_columns['close']
            
            # 计算移动平均线
            self.data['MA5'] = self.data[close_col].rolling(window=5).mean()
            self.data['MA10'] = self.data[close_col].rolling(window=10).mean()
            self.data['MA20'] = self.data[close_col].rolling(window=20).mean()
            self.data['MA60'] = self.data[close_col].rolling(window=60).mean()
            
            # 计算RSI
            delta = self.data[close_col].diff(1)
            gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
            loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
            RS = gain / loss
            self.data['RSI'] = 100 - (100 / (1 + RS))
            
            # 计算MACD
            exp1 = self.data[close_col].ewm(span=12, adjust=False).mean()
            exp2 = self.data[close_col].ewm(span=26, adjust=False).mean()
            self.data['MACD'] = exp1 - exp2
            self.data['Signal_Line'] = self.data['MACD'].ewm(span=9, adjust=False).mean()
            
            print("技术指标计算完成")
        
        return self.data
    
    def plot_price_trend(self, price_columns):
        """绘制价格趋势图"""
        if self.data is None:
            raise ValueError("请先加载数据")
            
        plt.figure(figsize=(14, 7))
        
        if 'close' in price_columns:
            plt.plot(self.data.index, self.data[price_columns['close']], label='收盘价', linewidth=2)
        
        # 绘制移动平均线
        if 'MA5' in self.data.columns:
            plt.plot(self.data.index, self.data['MA5'], label='MA5', alpha=0.7)
        if 'MA10' in self.data.columns:
            plt.plot(self.data.index, self.data['MA10'], label='MA10', alpha=0.7)
        if 'MA20' in self.data.columns:
            plt.plot(self.data.index, self.data['MA20'], label='MA20', alpha=0.7)
        if 'MA60' in self.data.columns:
            plt.plot(self.data.index, self.data['MA60'], label='MA60', alpha=0.7)
        
        plt.title('比亚迪股票价格趋势', fontsize=16)
        plt.xlabel('日期', fontsize=12)
        plt.ylabel('价格', fontsize=12)
        plt.grid(True, alpha=0.3)
        plt.legend()
        plt.tight_layout()
        
        # 保存图表
        save_path = os.path.join(self.figures_dir, 'byd_price_trend.png')
        plt.savefig(save_path, dpi=300)
        print(f"价格趋势图已保存至: {save_path}")
        plt.close()
    
    def plot_candlestick(self, price_columns):
        """绘制蜡烛图"""
        required_columns = ['open', 'high', 'low', 'close']
        if not all(col in price_columns for col in required_columns):
            print("缺少必要的价格列，无法绘制蜡烛图")
            return
        
        # 准备蜡烛图数据
        candle_data = self.data.copy()
        candle_data = candle_data.rename(columns={
            price_columns['open']: 'Open',
            price_columns['high']: 'High',
            price_columns['low']: 'Low',
            price_columns['close']: 'Close'
        })
        
        # 只选择最近200个交易日进行绘制
        if len(candle_data) > 200:
            candle_data = candle_data.iloc[-200:]
        
        # 添加成交量
        if 'volume' in price_columns:
            candle_data['Volume'] = self.data[price_columns['volume']]
        
        # 绘制蜡烛图
        mpf.plot(candle_data, type='candle', style=mpf_style,
                 title='比亚迪股票蜡烛图',
                 ylabel='价格',
                 ylabel_lower='成交量',
                 volume=True,
                 mav=(5, 10, 20),
                 savefig=os.path.join(self.figures_dir, 'byd_candlestick.png'))
        
        print(f"蜡烛图已保存至: {os.path.join(self.figures_dir, 'byd_candlestick.png')}")
    
    def plot_rsi(self):
        """绘制RSI指标图"""
        if 'RSI' not in self.data.columns:
            print("RSI指标不存在")
            return
        
        plt.figure(figsize=(14, 5))
        plt.plot(self.data.index, self.data['RSI'], label='RSI', linewidth=2)
        plt.axhline(y=70, color='r', linestyle='--', alpha=0.5, label='超买线')
        plt.axhline(y=30, color='g', linestyle='--', alpha=0.5, label='超卖线')
        plt.title('比亚迪股票RSI指标', fontsize=16)
        plt.xlabel('日期', fontsize=12)
        plt.ylabel('RSI值', fontsize=12)
        plt.grid(True, alpha=0.3)
        plt.legend()
        plt.tight_layout()
        
        # 保存图表
        save_path = os.path.join(self.figures_dir, 'byd_rsi.png')
        plt.savefig(save_path, dpi=300)
        print(f"RSI指标图已保存至: {save_path}")
        plt.close()
    
    def plot_macd(self):
        """绘制MACD指标图"""
        if 'MACD' not in self.data.columns or 'Signal_Line' not in self.data.columns:
            print("MACD指标不存在")
            return
        
        fig, ax1 = plt.subplots(figsize=(14, 5))
        
        # 绘制MACD和信号线
        ax1.plot(self.data.index, self.data['MACD'], label='MACD', linewidth=2)
        ax1.plot(self.data.index, self.data['Signal_Line'], label='信号线', linewidth=2)
        
        # 绘制MACD柱状图
        ax1.bar(self.data.index, self.data['MACD'] - self.data['Signal_Line'], label='MACD差', alpha=0.3)
        
        plt.title('比亚迪股票MACD指标', fontsize=16)
        plt.xlabel('日期', fontsize=12)
        plt.ylabel('MACD值', fontsize=12)
        plt.grid(True, alpha=0.3)
        plt.legend()
        plt.tight_layout()
        
        # 保存图表
        save_path = os.path.join(self.figures_dir, 'byd_macd.png')
        plt.savefig(save_path, dpi=300)
        print(f"MACD指标图已保存至: {save_path}")
        plt.close()
    
    def plot_volume(self, price_columns):
        """绘制成交量图"""
        if 'volume' not in price_columns:
            print("成交量数据不存在")
            return
        
        plt.figure(figsize=(14, 5))
        plt.bar(self.data.index, self.data[price_columns['volume']], label='成交量', alpha=0.7)
        plt.title('比亚迪股票成交量', fontsize=16)
        plt.xlabel('日期', fontsize=12)
        plt.ylabel('成交量', fontsize=12)
        plt.grid(True, alpha=0.3)
        plt.legend()
        plt.tight_layout()
        
        # 保存图表
        save_path = os.path.join(self.figures_dir, 'byd_volume.png')
        plt.savefig(save_path, dpi=300)
        print(f"成交量图已保存至: {save_path}")
        plt.close()
    
    def run_analysis(self):
        """运行完整分析流程"""
        # 1. 加载数据
        self.load_data()
        
        # 2. 数据清洗
        self.clean_data()
        
        # 3. 识别价格列
        price_columns = self.identify_price_columns()
        
        # 4. 计算技术指标
        self.calculate_indicators(price_columns)
        
        # 5. 生成可视化图表
        self.plot_price_trend(price_columns)
        self.plot_candlestick(price_columns)
        self.plot_rsi()
        self.plot_macd()
        self.plot_volume(price_columns)
        
        print("\n分析完成！所有图表已保存到figures目录。")
        
        # 6. 输出一些基本统计信息
        if 'close' in price_columns:
            close_col = price_columns['close']
            print("\n价格统计信息:")
            print(f"起始日期: {self.data.index.min()}")
            print(f"结束日期: {self.data.index.max()}")
            print(f"价格范围: {self.data[close_col].min():.2f} - {self.data[close_col].max():.2f}")
            print(f"平均价格: {self.data[close_col].mean():.2f}")
            print(f"价格中位数: {self.data[close_col].median():.2f}")

if __name__ == '__main__':
    # 文件路径
    file_path = 'd:\\financial-big-data\\比亚迪\\比亚迪后复权历史行情数据.xlsx'
    
    # 创建分析器实例并运行分析
    analyzer = BYDStockAnalyzer(file_path)
    analyzer.run_analysis()